#include"lcd_drv.h"

extern const uint8_t ascii06x12[95][12];
extern const uint8_t ascii08x16[95][16];
extern const uint8_t ascii12x24[95][48];

/**
 * @desc  : EXMC连接LCD引脚配置
 * @date  : 2025-05-29
 * @author: AnRui
 * @note  :  地址总线引脚 19个  EXMC_A0-> EXMC_A18
 *           数据总线引脚 16个  EXMC_D0-> EXMC_D15
 *           片选引脚 1个       EXMC_NE1
 *           读写控制2个        EXMC_NWE  EXMC_NOE
 */
static void GpioConfig(void) {
    /* 使能RCU相关时钟 */
    rcu_periph_clock_enable(RCU_GPIOD);  //使能GPIOD的时钟
    rcu_periph_clock_enable(RCU_GPIOE);  //使能GPIOE的时钟
    rcu_periph_clock_enable(RCU_GPIOF);  //使能GPIOF的时钟
    rcu_periph_clock_enable(RCU_GPIOG);  //使能GPIOG的时钟

    /* 地址总线 */
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_0); //A0
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_1); //A1
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_2); //A2
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_3); //A3
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_4); //A4
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_5); //A5
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_12); //A6
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_13); //A7
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_14); //A8
    gpio_init(GPIOF, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_15); //A9
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_0); //A10
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_1); //A11
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_2); //A12
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_3); //A13
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_4); //A14
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_5); //A15
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_11); //A16
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_12); //A17
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_13); //A18

    /* 数据总线 */
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_14); //A0
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_15); //A1
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_0); //A2
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_1); //A3
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_7); //A4
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_8); //A5
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_9); //A6
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_10); //A7
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_11); //A8
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_12); //A9
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_13); //A10
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_14); //A11
    gpio_init(GPIOE, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_15); //A12
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_8); //A13
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_9); //A14
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_10); //A15

    /* 控制信号 */
    gpio_init(GPIOG, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_9); //EXMC_NE1
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_4); //EXMC_NOE
    gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_5); //EXMC_NWE
}



/**
 * @desc  : EXMC配置
 * @date  : 2025-05-29
 * @author: AnRui
 * @note  :    1.采用类SRAM的方式控制外部LCD:模式A BANK0 Region1 访问的初始地址为:0x64000000
 *             2.读写模式配置:
 *                异步
 *                外部设备LCD读写循环的时间 差距大 所以读写分开配置 :设置的时候保证 EXMC读写数据的时间 要大于等于外部设备的读写时间 实际上经过验证 都配置的小一点
 *                读写循环时间不一致 需要分开配置
 */
static void ExmcConfig(void) {
    exmc_norsram_parameter_struct lcdInitStruct;
    exmc_norsram_struct_para_init(&lcdInitStruct);
    /* 使能EXMC时钟 */
    rcu_periph_clock_enable(RCU_EXMC);


    /* 读时序配置 */
    exmc_norsram_timing_parameter_struct lcdReadTimingInitStruct;
    lcdReadTimingInitStruct.asyn_access_mode = EXMC_ACCESS_MODE_A; //模式A，异步访问SRAM
    lcdReadTimingInitStruct.asyn_address_setuptime = 0;  //异步访问地址建立时间
    lcdReadTimingInitStruct.asyn_address_holdtime = 0;  //异步访问地址保持时间
    lcdReadTimingInitStruct.asyn_data_setuptime = 15; //异步访问数据建立时间
    lcdReadTimingInitStruct.bus_latency = 0;  //同步/异步访问总线延时时间
    lcdReadTimingInitStruct.syn_clk_division = 0;  //同步访问时钟分频系数（从HCLK中分频）
    lcdReadTimingInitStruct.syn_data_latency = 0;  //同步访问中获得第1个数据所需要的等待延迟

    /* 写时序配置 */
    exmc_norsram_timing_parameter_struct lcdWriteTimingInitStruct;
    lcdWriteTimingInitStruct.asyn_access_mode = EXMC_ACCESS_MODE_A; //模式A，异步访问SRAM
    lcdWriteTimingInitStruct.asyn_address_setuptime = 0; //异步访问地址建立时间
    lcdWriteTimingInitStruct.asyn_address_holdtime = 0; //异步访问地址保持时间
    lcdWriteTimingInitStruct.asyn_data_setuptime = 2; //异步访问数据建立时间 2
    lcdWriteTimingInitStruct.bus_latency = 0; //同步/异步访问总线延时时间 0
    lcdWriteTimingInitStruct.syn_clk_division = 0; //同步访问时钟分频系数（从HCLK中分频）
    lcdWriteTimingInitStruct.syn_data_latency = 0; //同步访问中获得第1个数据所需要的等待延迟


    /* Region1配置 */

    lcdInitStruct.norsram_region = EXMC_BANK0_NORSRAM_REGION1; //Region1
    lcdInitStruct.address_data_mux = DISABLE;                    //禁用地址、数据总线多路复用
    lcdInitStruct.memory_type = EXMC_MEMORY_TYPE_SRAM;      //储存器类型为SRAM
    lcdInitStruct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; //数据宽度16位
    lcdInitStruct.burst_mode = DISABLE;                    //禁用突发访问
    lcdInitStruct.wrap_burst_mode = DISABLE;                    //禁用包突发访问
    lcdInitStruct.asyn_wait = DISABLE;                    //禁用异步等待
    lcdInitStruct.extended_mode = ENABLE;                     //使能扩展模式 控制读写时序 是否独立配置
    lcdInitStruct.memory_write = ENABLE;                     //使能写入外部存储器
    lcdInitStruct.nwait_signal = DISABLE;                    //禁用等待输入信号
    lcdInitStruct.write_mode = EXMC_ASYN_WRITE;            //写入模式为异步写入
    lcdInitStruct.read_write_timing = &lcdReadTimingInitStruct;   //读时序配置
    lcdInitStruct.write_timing = &lcdWriteTimingInitStruct;  //写时序配置
    exmc_norsram_init(&lcdInitStruct);
    exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION1);
}



/**
 * @desc  : 背光初始化
 * @date  : 2025-06-03
 * @author: AnRui
 * @note  :
 */
static void BacklightConfig(void) {
    rcu_periph_clock_enable(RCU_GPIOA);
    gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_1);
}


/**
 * @desc  : LCD初始化
 * @date  : 2025-06-03
 * @author: AnRui
 * @note  :
 */
static void LcdInit(void) {
    uint16_t id;
    LcdSendCmd(LCD_READ_ID_CMD);
    id = LcdGetData();	    // dummy read
    id = LcdGetData();	    // 读到0X00
    id = LcdGetData();   	// 读取94
    id <<= 8;
    id |= LcdGetData();  	// 读取88
    printf("LCD ID:%x\n", id);

    if (id != LCD_ID) {
        printf("LCD init error\n");
        return;
    }
    // 设备厂商提供的初始化代码
    LcdSendCmd(0XF7);
    LcdSendData(0xA9);
    LcdSendData(0x51);
    LcdSendData(0x2C);
    LcdSendData(0x82);
    LcdSendCmd(0xC0);
    LcdSendData(0x11);
    LcdSendData(0x09);
    LcdSendCmd(0xC1);
    LcdSendData(0x41);
    LcdSendCmd(0XC5);
    LcdSendData(0x00);
    LcdSendData(0x0A);
    LcdSendData(0x80);
    LcdSendCmd(0xB1);
    LcdSendData(0xB0);
    LcdSendData(0x11);
    LcdSendCmd(0xB4);
    LcdSendData(0x02);
    LcdSendCmd(0xB6);
    LcdSendData(0x02);
    LcdSendData(0x22);
    LcdSendCmd(0xB7);
    LcdSendData(0xc6);
    LcdSendCmd(0xBE);
    LcdSendData(0x00);
    LcdSendData(0x04);
    LcdSendCmd(0xE9);
    LcdSendData(0x00);
    // 设置GRAMde 自增方向
    LcdSendCmd(0x36);
    // 0000 0000 1000 0000  高8位不用
    // 100代表从左到右 从上到下
    LcdSendData(0x08);
    LcdSendCmd(0x3A);
    LcdSendData(0x55);
    LcdSendCmd(0xE0);
    LcdSendData(0x00);
    LcdSendData(0x07);
    LcdSendData(0x10);
    LcdSendData(0x09);
    LcdSendData(0x17);
    LcdSendData(0x0B);
    LcdSendData(0x41);
    LcdSendData(0x89);
    LcdSendData(0x4B);
    LcdSendData(0x0A);
    LcdSendData(0x0C);
    LcdSendData(0x0E);
    LcdSendData(0x18);
    LcdSendData(0x1B);
    LcdSendData(0x0F);
    LcdSendCmd(0XE1);
    LcdSendData(0x00);
    LcdSendData(0x17);
    LcdSendData(0x1A);
    LcdSendData(0x04);
    LcdSendData(0x0E);
    LcdSendData(0x06);
    LcdSendData(0x2F);
    LcdSendData(0x45);
    LcdSendData(0x43);
    LcdSendData(0x02);
    LcdSendData(0x0A);
    LcdSendData(0x09);
    LcdSendData(0x32);
    LcdSendData(0x36);
    LcdSendData(0x0F);
    LcdSendCmd(0x11);
    LcdSendCmd(0x29);
}

/**
 * @desc  : LCD驱动初始化
 * @date  : 2025-06-03
 * @author: AnRui
 * @note  :
 */
void LcdDrvInit(void) {
    GpioConfig();
    ExmcConfig();
    BacklightConfig();
    LcdInit();
    LCD_BACKLIGHT_ON();
}

/**
 * @desc  : 设置LCD显示窗口区域（指定绘图的起始和结束坐标）
 * @date  : 2025-06-03
 * @author: AnRui
 * @param : sx - 显示区域的起始X坐标（左上角）
 * @param : sy - 显示区域的起始Y坐标（左上角）
 * @param : ex - 显示区域的结束X坐标（右下角）
 * @param : ey - 显示区域的结束Y坐标（右下角）
 * @note  :
 *         1. 坐标范围取决于LCD分辨率（如240x320的屏幕，x范围0~239，y范围0~319）
 *         2. 调用此函数后，后续的像素写入将仅限于此区域内
 *         3. 坐标值需拆分为高8位和低8位分两次发送（大端格式）
 */
static void LcdSetShowRange(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey) {

    // 设置X方向的显示范围
    LcdSendCmd(LCD_X_CMD);      // 发送设置X坐标的命令
    LcdSendData(sx >> 8);       // 发送起始X坐标的高8位
    LcdSendData(sx & 0XFF);     // 发送起始X坐标的低8位
    LcdSendData(ex >> 8);       // 发送结束X坐标的高8位
    LcdSendData(ex & 0XFF);     // 发送结束X坐标的低8位

    // 设置Y方向的显示范围
    LcdSendCmd(LCD_Y_CMD);      // 发送设置Y坐标的命令
    LcdSendData(sy >> 8);       // 发送起始Y坐标的高8位
    LcdSendData(sy & 0XFF);     // 发送起始Y坐标的低8位
    LcdSendData(ey >> 8);       // 发送结束Y坐标的高8位
    LcdSendData(ey & 0XFF);     // 发送结束Y坐标的低8位
}

/**
 * @desc  : 在LCD指定区域填充纯色
 * @date  : 2025-06-03
 * @author: AnRui
 * @param : sx, sy - 矩形区域左上角坐标
 * @param : ex, ey - 矩形区域右下角坐标
 * @param : color  - 填充颜色（16位RGB565格式）
 * @note  :
 *         1. 坐标范围需在LCD有效区域内
 *         2. 颜色格式为RGB565: R[15:11], G[10:5], B[4:0]
 *         3. 填充过程中会锁定显示窗口，直到填充完成
 */
void LcdFillPureColor(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color) {
    // 计算填充区域的总像素数
    uint32_t totalPoint = (ex - sx + 1) * (ey - sy + 1);
    // 设置显示窗口（限制后续绘图范围）
    LcdSetShowRange(sx, sy, ex, ey);
    // 发送写GRAM命令（开始写入像素数据） 0x2C
    LcdSendCmd(LCD_WG_CMD);
    // 循环填充每个像素
    for (uint32_t i = 0; i < totalPoint; i++) {
        LcdSendData(color);
    }
}



/**
 * @desc  : 在LCD屏幕上指定区域填充多色彩数据
 * @date  : 2025-06-03
 * @author: AnRui
 * @param : sx - 起始点X坐标（左上角）
 * @param : sy - 起始点Y坐标（左上角）
 * @param : ex - 结束点X坐标（右下角）
 * @param : ey - 结束点Y坐标（右下角）
 * @param : color - 指向颜色数据数组的指针，需确保数组大小不小于填充区域总像素数
 * @note  :
 *        1. 坐标必须满足 sx ≤ ex 且 sy ≤ ey，否则函数将直接返回
 *        2. 函数假设color数组长度足够，调用者需自行保证数据有效性
 *        3. 填充方向为从左到右、从上到下的扫描线顺序
 *        4. 色彩格式取决于LCD控制器配置，通常为RGB565格式
 */
void LcdFillMultiColor(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t* color) {
    // 验证输入参数有效性，防止无效坐标或空指针导致的问题
    if (sx > ex || sy > ey || color == NULL) {
        return; // 错误处理：参数无效时直接返回
    }
    // 正确的边界检查：x坐标与宽度比较，y坐标与高度比较
    if (sx >= LCD_PIXEL_WIDTH || sy >= LCD_PIXEL_HEIGHT ||
        ex >= LCD_PIXEL_WIDTH || ey >= LCD_PIXEL_HEIGHT) {
        return;
    }

    // 计算填充区域的总像素数
    uint32_t totalPoint = (ex - sx + 1) * (ey - sy + 1);
    // 设置LCD控制器的写区域为指定矩形范围
    LcdSetShowRange(sx, sy, ex, ey);
    // 发送写GRAM命令，准备开始像素数据写入
    LcdSendCmd(LCD_WG_CMD);
    // 按扫描线顺序填充色彩数据
    for (uint32_t i = 0; i < totalPoint; i++) {
        LcdSendData(color[i]);
    }

}

/**
 * @desc  : lcd 使用dma的发生发送数据
 * @date  : 2025-06-16
 * @author: AnRui
 * @param : sx - {参数说明}:
 * @param : sy - {参数说明}:
 * @param : ex - {参数说明}:
 * @param : ey - {参数说明}:
 * @note  : 具体的发送代码 由lvgl控制  这里只是做发送数据之前的配置
 */
void LcdFillMultiColorByDma(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey) {
    // 设置LCD控制器的写区域为指定矩形范围
    LcdSetShowRange(sx, sy, ex, ey);
    // 发送写GRAM命令，准备开始像素数据写入
    LcdSendCmd(LCD_WG_CMD);
}



/**
 * @desc  : 在LCD屏幕上指定位置绘制单个像素点
 * @date  : 2025-06-03
 * @author: AnRui
 * @param : xPos - 起始点X坐标（左上角）
 * @param : yPos - 起始点Y坐标（左上角）
 * @param : color - 像素点的颜色值（通常为RGB565格式）
 * @note  : 左上角的坐标与左下角的坐标一致  右边也一样
 */
void LcdDrawPoint(uint16_t xPos, uint16_t yPos, uint16_t color) {
    // 设置写区域为单个像素点 (xPos, yPos)
    LcdSetShowRange(xPos, yPos, xPos, yPos);
    // 发送写GRAM命令，指示LCD控制器准备接收像素数据
    LcdSendCmd(LCD_WG_CMD);
    // 写入像素颜色值到当前设置的坐标位置
    LcdSendData(color);
}


/**
 * @desc  : 清屏
 * @date  : 2025-06-03
 * @author: AnRui
 * @param : color - {参数说明}:
 * @note  : 用某个颜色填充整个屏幕
 */
void LcdClear(uint16_t color) {
    LcdFillPureColor(0, 0, LCD_PIXEL_WIDTH - 1, LCD_PIXEL_HEIGHT - 1, color);
}



/**
 * @desc  : 在LCD上显示ACSII字符
 * @date  : 2025-06-04
 * @author: AnRui
 * @param : xPos - {参数说明}: 起始点X坐标（左上角）
 * @param : yPos - {参数说明}: 起始点Y坐标（左上角）
 * @param : FontInfoStruct - {参数说明}: 字体信息结构体 包含字体背景色 字体颜色 字体大小
 * @note  : 如果字体的大小不是8的倍数 会自动补0 在发送的时候要注意去调
 *					尽量保证函数不超过4个参数
 */
void LcdShowAscii(uint16_t xPos, uint16_t yPos, char font, FontInfoStruct* fontInfo) {
    uint16_t arrRowIndex = font - ' ';
    uint16_t fontWidth = fontInfo->fontSize / 2;
    uint16_t fontHeight = fontInfo->fontSize;
    uint16_t fontHasBytes = (fontWidth / 8 + ((fontWidth % 8) ? 1 : 0)) * fontHeight;  // 得到一个字符所占的字节数

    const uint8_t* charArr = NULL;
    if (fontInfo->fontSize == ASCII_FONT_SIZE_12) {
        charArr = &ascii06x12[arrRowIndex][0];
    } else if (fontInfo->fontSize == ASCII_FONT_SIZE_16) {
        charArr = &ascii08x16[arrRowIndex][0];
    } else if (fontInfo->fontSize == ASCII_FONT_SIZE_24) {
        charArr = &ascii12x24[arrRowIndex][0];
    } else {
        return;
    }
    //设置显示区域
    LcdSetShowRange(xPos, yPos, xPos + fontWidth - 1, yPos + fontHeight - 1);
    LcdSendCmd(LCD_WG_CMD);
    //读取数组值 向LCD发送数据
    uint8_t data = 0;
    uint16_t sendByteNum = 0;
    // 循环该字体占用的所有字节
    for (int i = 0; i < fontHasBytes; i++) {
        data = charArr[i];
        // 循环每个字节中的每一位
        for (int j = 0; j < 8; j++) {
            if (data & 0x80) {
                LcdSendData(fontInfo->fontColor);
            } else {
                LcdSendData(fontInfo->bgColor);
            }
            data <<= 1;
            // 8位数据中，要过滤掉补齐的位数
            sendByteNum++;
            if (sendByteNum == fontWidth) {
                sendByteNum = 0;
                break;
            }
        }
    }
}


/**
 * @desc  : 在LCD上显示ASCII字符串
 * @date  : 2025-06-04
 * @author: AnRui
 * @param : xPos - {参数说明}:
 * @param : yPos - {参数说明}:
 * @param : string - {参数说明}:
 * @param : fontInfo - {参数说明}:
 * @note  :
 */
void LcdShowAsciiString(uint16_t xPos, uint16_t yPos, char* string, FontInfoStruct* fontInfo) {
    uint16_t fontWidth = fontInfo->fontSize / 2;
    uint16_t fontHeight = fontInfo->fontSize;
    while (*string != '\0') {
        // 是否换行
        if (*string == '\n') {
            xPos = 0;
            yPos += fontHeight;
            string++;
            // 换行符不显示
            continue;
        }
        if (xPos + fontWidth - 1 > LCD_PIXEL_WIDTH - 1) {
            xPos = 0;
            yPos += fontHeight;
        }
        //超过屏幕显示
        if (yPos > LCD_PIXEL_HEIGHT - 1) {
            return;
        }
        // 显示
        LcdShowAscii(xPos, yPos, *string, fontInfo);
        // 计算下一个字符的左上角的横坐标
        xPos += fontWidth;
        string++;
    }
}


/**
 * @desc  : 在LCD屏幕上显示中文字符
 * @date  : 2025-06-04
 * @author: AnRui
 * @param : xPos - {参数说明}: 字符显示区域左上角的X坐标
 * @param : yPos - {参数说明}: 字符显示区域左上角的Y坐标
 * @param : fontArr - {参数说明}: 指向中文字符点阵数据的指针
 * @param : fontInfo - {参数说明}: 指向字体信息结构体的指针，包含字体大小、颜色等信息
 * @note  :
 */
void LcdShowChinese(uint16_t xPos, uint16_t yPos, uint8_t* fontArr, FontInfoStruct* fontInfo) {
    uint16_t fontWidth = fontInfo->fontSize;
    uint16_t fontHeight = fontInfo->fontSize;
    uint16_t fontHasBytes = (fontWidth / 8 + ((fontWidth % 8) ? 1 : 0)) * fontHeight;  // 得到一个字符所占的字节数
    //设置显示区域
    LcdSetShowRange(xPos, yPos, xPos + fontWidth - 1, yPos + fontHeight - 1);
    LcdSendCmd(LCD_WG_CMD);
    //读取数组值 向LCD发送数据
    uint8_t data = 0;
    uint16_t sendByteNum = 0;
    // 循环该字体占用的所有字节
    for (int i = 0; i < fontHasBytes; i++) {
        data = fontArr[i];
        // 循环每个字节中的每一位
        for (int j = 0; j < 8; j++) {
            if (data & 0x80) {
                LcdSendData(fontInfo->fontColor);
            } else {
                LcdSendData(fontInfo->bgColor);
            }
            data <<= 1;
            // 8位数据中，要过滤掉补齐的位数
            sendByteNum++;
            if (sendByteNum == fontWidth) {
                sendByteNum = 0;
                break;
            }
        }
    }
}


